#ifndef INFINITY_GRAPHICS_PIPELINE_H
#	define INFINITY_GRAPHICS_PIPELINE_H

#include "service.h"

#include "graphicsmodel.h"
#include "graphicsmesh.h"

#include "servicecamera.h"

namespace infinity {
	namespace graphics {

class Pipeline
{
public : //! @name Types definitions
	//! @{
	struct Setup
	{
		Setup() : light(NULL), texture(NULL) { ; }
		dw::graphics::Light* light;
		dw::graphics::ITexture* texture;
		dw::graphics::Renderer::State states;
	};

	typedef std::set< Model* > ModelsCollection;
	//! @}

public : //! @name Construction & Destruction
	//! @{
	Pipeline(const std::string& name)
	:	m_name(name)
	,	m_visible(true)
	{
	}

	~Pipeline()
	{
		m_models.clear();
	}
	//! @}

private : //! @name Commands
	//! @{
	void addModel(Model* model)
	{
		dwAssert(model != NULL, "Invalid parameter: model is null");

		m_models.insert(model);
	}

	void removeModel(Model* model)
	{
		dwAssert(model != NULL, "Invalid parameter: model is null");

		m_models.erase(model);
	}

	void render()
	{
		dw::graphics::Renderer& renderer = dw::graphics::GraphicsManager::getInstance().getRenderer();

		if (m_setup.light != NULL)
		{
			ServiceCamera& svcCamera = Application::getInstance().getService<ServiceCamera>();
			dw::graphics::Camera* camera = svcCamera.getActiveCameraController()->getCamera();
			dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();

			const dw::maths::vec4& camPos = cameraNode->getTransform().pos;

			m_setup.light->setPosition(dw::maths::Point4f(camPos.x, camPos.y, camPos.z, 1.0f));

			renderer.setLight(0, m_setup.light);
		}

		if (m_setup.texture != NULL)
		{
			renderer.setTexture(0, m_setup.texture);

			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		}

		renderer.pushState();
		renderer.setState(m_setup.states);

		renderer.setMatrixMode(dw::graphics::Renderer::MODELVIEW);
		renderer.pushMatrix();

		ModelsCollection::const_iterator modelsEnd = m_models.end();
		for (ModelsCollection::iterator modelIt = m_models.begin(); modelIt != modelsEnd; ++modelIt)
		{
			dwAssert(*modelIt != NULL, "Invalid internal state");

			const Model* model = *modelIt;

			renderer.pushMatrix();
			renderer.multMatrix(model->getTransform());

			model->getMesh()->render();
			
			renderer.popMatrix();
		}

		renderer.popMatrix();
		renderer.popState();

		if (m_setup.texture != NULL)
		{
			renderer.setTexture(0, 0);
			// renderer.setTextureUnitState(0, m_setup.texturestate);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		}

		if (m_setup.light != NULL)
		{
			renderer.setLight(0, 0);
		}
	}

	friend class ServiceGraphics;
	//! @}

public :
	bool isVisible() const { return m_visible; }
	void setVisible(bool visible) { m_visible = visible; }
	Setup& getSetup() { return m_setup; }

private :
	std::string m_name;
	bool m_visible;
	Setup m_setup;
	ModelsCollection m_models;
};

	} // namespace graphics
} // namespace infinity

#endif // !INFINITY_GRAPHICS_PIPELINE_H
